﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SqlCeProvider.Enums;

namespace DatabaseExtension.ConnectionString
{
    /// <summary>
    /// Provide important connection information for Sql Compact Edition Database driver.
    /// <see cref="http://msdn.microsoft.com/en-us/library/system.data.sqlserverce.sqlceconnectionstringbuilder%28v=vs.100%29.aspx"/>
    /// </summary>
    public sealed class SqlCeConnectionInfo : ConnectionInfo
    {
        public System.Data.SqlServerCe.SqlCeConnectionStringBuilder SqlCeConnectionStringBuilder
        {
            get
            {
                return (base.ConnectionStringBuilder as System.Data.SqlServerCe.SqlCeConnectionStringBuilder);
            }
        }
        #region ConnectionStringBuilder Derived Parameter
        /// <summary>
        /// The percent of free space in the database file that is allowed before autoshrink begins.
        /// A value of 100 disables autoshrink. If not specified, the default value is 60.
        /// </summary>
        public int AutoshrinkThreshold
        {
            get
            {
                return this.SqlCeConnectionStringBuilder.AutoshrinkThreshold;
            }
            set
            {
                this.SqlCeConnectionStringBuilder.AutoshrinkThreshold = value;
            }
        }
        /// <summary>
        /// Gets or sets a value that indicates whether or not the database collation is case-sensitive.
        /// </summary>
        public Boolean CaseSensitive
        {
            get
            {
                return this.SqlCeConnectionStringBuilder.CaseSensitive;
            }
            set
            {
                this.SqlCeConnectionStringBuilder.CaseSensitive = value;
            }
        }
        /// <summary>
        /// The number of locks a transaction will acquire before attempting escalation from row to page, 
        /// or from page to table. If not specified, the default value is 100.
        /// </summary>
        public int DefaultLockEscalation
        {
            get
            {
                return this.SqlCeConnectionStringBuilder.DefaultLockEscalation;
            }
            set
            {
                this.SqlCeConnectionStringBuilder.DefaultLockEscalation = value;
            }
        }
        /// <summary>
        /// The default number of milliseconds that a transaction will wait for a lock. 
        /// If not specified, the default value is 2000.
        /// </summary>
        public int DefaultLockTimeout
        {
            get
            {
                return this.SqlCeConnectionStringBuilder.DefaultLockTimeout;
            }
            set
            {
                this.SqlCeConnectionStringBuilder.DefaultLockTimeout = value;
            }
        }
        /// <summary>
        /// A Boolean value that determines whether or not the database is encrypted. 
        /// Must be set to true to enable encryption or false for no encryption. 
        /// If not specified, the default value is false. If you enable encryption, 
        /// you must also specify a password with the password property. 
        /// If you specify a password, encryption is enabled regardless of how you set this property.
        /// </summary>
        public Boolean Encrypt
        {
            get
            {
                return this.SqlCeConnectionStringBuilder.Encrypt;
            }
            set
            {
                this.SqlCeConnectionStringBuilder.Encrypt = value;
            }
        }
        /// <summary>
        /// Gets or sets the encryption mode with which the database is to be created against.
        /// </summary>
        public SqlCeEnryptionMode EncryptionMode
        {
            get
            {
                return (SqlCeEnryptionMode)this.SqlCeConnectionStringBuilder.EncryptionMode.StringToEnum(typeof(SqlCeEnryptionMode));
            }
            set
            {
                this.SqlCeConnectionStringBuilder.EncryptionMode = value.EnumToString();
            }
        }
        /// <summary>
        /// true if a connection database is opened by using Enlist, otherwise, false.
        /// </summary>
        public Boolean Enlist
        {
            get
            {
                return this.SqlCeConnectionStringBuilder.Enlist;
            }
            set
            {
                this.SqlCeConnectionStringBuilder.Enlist = value;
            }
        }
        /// <summary>
        /// true if a connection database is opened by using Enlist, otherwise, false.
        /// </summary>
        public int FileAccessRetryTimeout
        {
            get
            {
                return this.SqlCeConnectionStringBuilder.FileAccessRetryTimeout;
            }
            set
            {
                this.SqlCeConnectionStringBuilder.FileAccessRetryTimeout = value;
            }
        }
        /// <summary>
        /// The mode to use when opening the database file. 
        /// For valid values, see the table that follows. 
        /// If not specified, the default value is 'Read Write'.
        /// </summary>
        public SqlCeFileMode FileMode
        {
            get
            {
                return (SqlCeFileMode)this.SqlCeConnectionStringBuilder.FileMode.StringToEnum(typeof(SqlCeFileMode));
            }
            set
            {
                this.SqlCeConnectionStringBuilder.FileMode = value.EnumToString();
            }
        }
        /// <summary>
        /// Specified the interval time (in seconds) before all committed transactions are flushed to disk. 
        /// If not specified, the default value is 10.
        /// </summary>
        public int FlushInterval
        {
            get
            {
                return this.SqlCeConnectionStringBuilder.FlushInterval;
            }
            set
            {
                this.SqlCeConnectionStringBuilder.FlushInterval = value;
            }
        }
        /// <summary>
        /// The locale ID (LCID) to use with the database. Valid LCID values are listed in the CultureInfo class.
        /// </summary>
        public System.Globalization.CultureInfo LocaleIdentifier
        {
            get
            {
                return new System.Globalization.CultureInfo(this.SqlCeConnectionStringBuilder.InitialLcid);
            }
            set
            {
                this.SqlCeConnectionStringBuilder.InitialLcid = value.LCID;
            }
        }
        /// <summary>
        /// The largest amount of memory, in kilobytes, 
        /// that SQL Server Compact Edition can use before 
        /// it starts flushing changes to disk. 
        /// If not specified, the default value is 640.
        /// </summary>
        public int MaxBufferSize
        {
            get
            {
                return this.SqlCeConnectionStringBuilder.MaxBufferSize;
            }
            set
            {
                this.SqlCeConnectionStringBuilder.MaxBufferSize = value;
            }
        }
        /// <summary>
        /// The maximum size of the database, in Megabytes. If not specified, the default value is 128.
        /// </summary>
        public int MaxDatabaseSize
        {
            get
            {
                return this.SqlCeConnectionStringBuilder.MaxDatabaseSize;
            }
            set
            {
                this.SqlCeConnectionStringBuilder.MaxDatabaseSize = value;
            }
        }
        /// <summary>
        /// When set to false (which is strongly recommended), 
        /// security-sensitive information, such as the password, 
        /// is not returned as part of the connection if the connection is open or has ever been in an open state. 
        /// Resetting the connection string resets all connection string values, including the password. The default value is false.
        /// </summary>
        public Boolean PersistSecurityInfo
        {
            get
            {
                return this.SqlCeConnectionStringBuilder.PersistSecurityInfo;
            }
            set
            {
                this.SqlCeConnectionStringBuilder.PersistSecurityInfo = value;
            }
        }
        /// <summary>
        /// The maximum size of the temporary database file, in Megabytes. If not specified, the default value is 128.
        /// </summary>
        public int TempFileMaxSize
        {
            get
            {
                return this.SqlCeConnectionStringBuilder.TempFileMaxSize;
            }
            set
            {
                this.SqlCeConnectionStringBuilder.TempFileMaxSize = value;
            }
        }
        /// <summary>
        /// The location of the temporary database. 
        /// If not specified, the default is to use the database specified in the data source property for temporary storage.
        /// </summary>
        public String TempFilePath
        {
            get
            {
                return this.SqlCeConnectionStringBuilder.TempFilePath;
            }
            set
            {
                this.SqlCeConnectionStringBuilder.TempFilePath = value;
            }
        }
        #endregion
        /// <summary>
        /// Manage SqlCe Boolean through the Enumerator Flags. Set only if needed. Not setted flags are marked as False Values.
        /// </summary>
        public SqlCeFlags BooleanFlags
        {
            get
            {
                return this._BooleanFlags;
            }
            set
            {
                var tmp = this.SqlCeConnectionStringBuilder;
                tmp.CaseSensitive = value.HasFlag(SqlCeFlags.CaseSensitive);
                tmp.Encrypt = value.HasFlag(SqlCeFlags.Encrypt);
                tmp.Enlist = value.HasFlag(SqlCeFlags.Enlist);
                tmp.PersistSecurityInfo = value.HasFlag(SqlCeFlags.PersistSecurityInfo);
                this._BooleanFlags = value;
            }
        }
        private SqlCeFlags _BooleanFlags;
        /// <summary>
        /// Get the connection String.
        /// </summary>
        public override string ConnectionString
        {
            get
            {
                if (this.LoadConnection != null)
                    this.LoadConnection(this, EventArgs.Empty);
                return base.ConnectionString;
            }
        }

        #region File is missing handlers
        /// <summary>
        /// Event for FailIfMissing Property (true create new database file)
        /// </summary>
        private event EventHandler<EventArgs> LoadConnection;
        /// <summary>
        /// Event Handler that create new SqlCe file if this does not exists.
        /// </summary>
        private void SqlCeConnectionInfo_LoadConnection(object sender, EventArgs e)
        {
            if (System.IO.File.Exists(this.Server) == false)
            {
                using (var ce = new System.Data.SqlServerCe.SqlCeEngine(this.ConnectionStringBuilder.ConnectionString))
                {
                    ce.CreateDatabase();
                }
            }
        }
        /// <summary>
        ///  True - Don't create the database if it does not exist, throw an error instead
        ///  False - Automatically create the database if it does not exist
        ///  Default value is true
        /// </summary>
        public Boolean FailIfMissing
        {
            get
            {
                return this._FailIfMissing;
            }
            set
            {
                if (value == false)
                {
                    if (this.LoadConnection == null)
                        this.LoadConnection += SqlCeConnectionInfo_LoadConnection;
                }
                else
                {
                    if (this.LoadConnection != null)
                        this.LoadConnection -= SqlCeConnectionInfo_LoadConnection;
                }
                this._FailIfMissing = value;
            }
        }
        private Boolean _FailIfMissing;
        #endregion

        /// <summary>
        /// Create new encrpyted SQL Compact Edition connection.
        /// </summary>
        /// <param name="server">Path to the database.</param>
        /// <param name="password">Encryption password.</param>
        public SqlCeConnectionInfo(String server, String password)
            : base(Databases.SQLCompact, server, null, password)
        {
            this.FailIfMissing = true;
        }
        /// <summary>
        /// Create new SQL Compact Edition Connection without Encryption.
        /// </summary>
        /// <param name="server"></param>
        public SqlCeConnectionInfo(String server)
            : this(server, null)
        {
        }
        /// <summary>
        /// Creat new SqlCeConnectionInfo based on Connection String Builder.
        /// </summary>
        /// <param name="cecsb">SQL Compact Edition Connection String Builder</param>
        public SqlCeConnectionInfo(System.Data.SqlServerCe.SqlCeConnectionStringBuilder cecsb)
            : base(Databases.SQLCompact, cecsb)
        {
            this.FailIfMissing = true;
        }
        /// <summary>
        /// Create new SqlCe Connection String Builder
        /// </summary>
        /// <returns></returns>
        protected override System.Data.Common.DbConnectionStringBuilder GetConnectionStringbuilder()
        {
            this.ClientFactory = System.Data.SqlServerCe.SqlCeProviderFactory.Instance;
            var result = this.ClientFactory.CreateConnectionStringBuilder() as System.Data.SqlServerCe.SqlCeConnectionStringBuilder;
            result.DataSource = this.Server;
            if (String.IsNullOrEmpty(base.Password) == false)
                result.Password = this.Password;
            return result;
        }
        /// <summary>
        /// Create new Db Parameter
        /// </summary>
        /// <param name="name">Parameter Name</param>
        /// <param name="value">Parameter Value</param>
        /// <param name="type">Use System.Data.SqlDbType for inserted parameter</param>
        /// <param name="isNullable">if true, empty object will be converted to DbNull.Value</param>
        /// <param name="Size">Object Size.</param>
        /// <returns>Return new DbParameter</returns>
        public override System.Data.Common.DbParameter CreateParameter(string name, object value, object type, bool isNullable, int Size = 0)
        {
            var param = this.ClientFactory.CreateParameter() as System.Data.SqlServerCe.SqlCeParameter;
            param.ParameterName = name;
            param.Value = (isNullable && value == null) ? DBNull.Value : value;
            param.Size = Size;
            if (type is System.Data.SqlDbType)
                param.SqlDbType = (System.Data.SqlDbType)type;
            else if (type is System.Data.DbType)
                param.DbType = (System.Data.DbType)type;
            else
                throw new ArgumentException("Could not recognize Db Type");
            param.IsNullable = isNullable;
            return param;
        }
        /// <summary>
        /// Create new Db Parameter. Autodetect parameter value type
        /// </summary>
        /// <param name="name">Parameter Name</param>
        /// <param name="value">Parameter Value</param>
        /// <param name="isNullable">if true, empty object will be converted to DbNull.Value</param>
        /// <param name="Size">Object Size.</param>
        /// <returns>Return new DbParameter</returns>
        public override System.Data.Common.DbParameter CreateParameter(string name, object value, bool isNullable, int Size = 0)
        {
            return this.CreateParameter(name, value, DbTypeConverter.ToDbType(value), isNullable, Size);
        }
    }
}
